package repl

import (
	"dolphin_plus/compiler"
	"dolphin_plus/lexer"
	"dolphin_plus/object"
	"dolphin_plus/parser"
	"dolphin_plus/vm"
	"fmt"
	"github.com/peterh/liner"
	"io"
	"os"
)

const PROMPT = ">>"

func Start() {
	line := liner.NewLiner()
	defer line.Close()
	line.SetCtrlCAborts(true)
	historyFile := "repl_history"
	if f, err := os.Open(historyFile); err == nil {
		line.ReadHistory(f)
		f.Close()
	}
	defer func() {
		if f, err := os.Create(historyFile); err == nil {
			line.WriteHistory(f)
			f.Close()
		}
	}()

	constants := []object.Object{}
	globals := make([]object.Object, vm.GlobalSize)
	symbolTable := compiler.NewSymbolTable()
	for i, v := range object.Builtins {
		symbolTable.DefineBuiltin(i, v.Name)
	}
	for {
		input, err := line.Prompt(PROMPT)
		if err != nil {
			if err == liner.ErrPromptAborted {
				fmt.Println("\nExiting REPL...")
				return
			}
			fmt.Fprintf(os.Stderr, "Error reading line: %s\n", err)
			continue
		}
		//if input == "exit()" {
		//	fmt.Println("Exiting REPL...")
		//	return
		//}
		line.AppendHistory(input)
		l := lexer.New(input)
		p := parser.New(l)

		program := p.ParseProgram()
		if len(p.Errors()) != 0 {
			printParserErrors(os.Stdout, p.Errors())
			continue
		}

		comp := compiler.NewWithState(symbolTable, constants)
		err = comp.Compile(program)
		if err != nil {
			fmt.Fprintf(os.Stdout, "Woops! Compilation failed: %s\n", err)
			continue
		}
		code := comp.Bytecode()
		constants = code.Constants // 应该是留给下次循环给编译器使用的constants  换言之更新constants的引用
		machine := vm.NewWithGlobalsStore(code, globals)
		err = machine.Run()
		if err != nil {
			fmt.Fprintf(os.Stdout, "Woops! Executing bytecode failed:\n %s\n", err)
			continue
		}
		stackTop := machine.LastPoppedStackElem()
		if stackTop == nil { // fix bug : when stack top is nil code should ignore it
			continue
		}
		io.WriteString(os.Stdout, stackTop.Inspect())
		io.WriteString(os.Stdout, "\n")
	}
}

func printParserErrors(out io.Writer, errors []string) {
	for _, msg := range errors {
		io.WriteString(out, "\t"+msg+"\n")
	}
}
